#include "AskalDataCache.h"
#include "hltypes/hfile.h"
#include "Bone.h"
#include "gtypes/Quaternion.h"
#include "iostream"


namespace askal
{

	AskalDataCache* AskalDataCache::ms_instance = 0;

	AskalDataCache::AskalDataCache()
	{
	}

	AskalDataCache::~AskalDataCache()
	{
		for(hmap<hstr, Skeleton*>::iterator it = _mSkeletonCache.begin(); it != _mSkeletonCache.end(); it++)
			delete it->second;
		for(hmap<hstr, AprilMesh*>::iterator it = _mMeshCache.begin(); it != _mMeshCache.end(); it++)
			delete it->second;
			
		_mSkeletonCache.clear();
		_mMeshCache.clear();
		
		Release();
	}

	AskalDataCache* AskalDataCache::Instance()
	{
		if(ms_instance == 0){
			ms_instance = new AskalDataCache();
		}
		return ms_instance;
	}

	void AskalDataCache::Release()
	{
		if(ms_instance){
			delete ms_instance;
		}
		ms_instance = 0;
	}
	
	Skeleton* AskalDataCache::getSkeleton(chstr name)
	{
		if(_mSkeletonCache.has_key(name))
			return _mSkeletonCache[name];
		return NULL;
	}
	
	AprilMesh* AskalDataCache::getMesh(chstr name)
	{
		if(_mMeshCache.has_key(name))
			return _mMeshCache[name];
		return NULL;
	}
	
	void AskalDataCache::loadMeshAndSkeletonData(chstr meshname)
	{
		April::rendersys->logMessage(" Reading data for " + meshname, "[askal]");
		
		hfile f;
		f.open(meshname);
		harray<hstr> lines = f.read_lines();
		f.close();
		
		Skeleton *s = NULL;
		AprilMesh *m = NULL;
		hstr animname, group;
		float duration;
		int keyframes;		
		
		int state = 0;
		
		/*
		 *  0 = 'None'
		 *  1 = 'Skeleton'
		 *  2 = 'Mesh'
		 *  3 = 'Animations'
		 * 
		 */
		
		for(int i = 0; i < lines.size(); ++i)
		{
			if(lines[i] == "skeleton_begin")
			{
				state = 1;
			} else if(lines[i] == "skeleton_end")
			{
				state = 0;
			} else if(lines[i] == "animations_begin")
			{
				state = 3;
			} else if(lines[i] == "animations_end")
			{
				state = 0;
			} else if(lines[i] == "mesh_begin")
			{
				state = 2;
			} else if(lines[i] == "mesh_end")
			{
				state = 0;
			}
			
			
			if((lines[i] != "skeleton_begin") && (lines[i] != "animations_begin") && (lines[i] != "mesh_begin"))
			{
				if(state == 1)
				{
					harray<hstr> ret = lines[i].split(" ");
					if(ret[0] == "n")
						if(s == NULL)
						{
							s = new Skeleton();
							s->mName = ret[1];
							_mSkeletonCache[ret[1]] = s;
						}
					if(ret[0] == "b")
					{
						Bone *b = new Bone();
						b->mName = ret[1];
						b->mOrientation.w = float(ret[2]);
						b->mOrientation.x = float(ret[3]);
						b->mOrientation.y = float(ret[4]);
						b->mOrientation.z = float(ret[5]);
						b->mTail.x = float(ret[6]);
						b->mTail.y = float(ret[7]);
						b->mTail.z = float(ret[8]);
						
						b->mTail = b->mOrientation.getConjugate() * gquat(b->mTail, 0) * b->mOrientation;
						
						s->addBone(*b);
						//std::cerr << s->getBone(ret[1])->mName.c_str() << std::endl;
					}
					if(ret[0] == "bp")
					{
						if(ret[2] == "NULL")
						{
							s->getBone(ret[1])->mParent = NULL;
						}
						else
						{
							s->getBone(ret[1])->mParent = s->getBone(ret[2]);
						}
						//std::cerr << "bone " << s->getBoneIndex(ret[1]) << " parent " << s->getBone(ret[1])->mParentIndex << std::endl;
					}
					if(ret[0] == "bc")
					{
						s->getBone(ret[1])->mChildren += s->getBone(ret[2]);
					}
				} else if(state == 3)
				{
					
					
					harray<hstr> ret = lines[i].split(" ");
					if(ret[0] == "a")
					{
						duration = 0.0;
						keyframes = 0;
						group = "NULL";
						animname = ret[1];
						if(!s->mAnimations.has_key(animname))
						{
							askal::Animation a;
							s->mAnimations[animname] = a;
						}
						s->mAnimations[animname].mAnimationName = animname;
						
					}
					if(ret[0] == "l")
					{
						duration = float(ret[1]);
						s->mAnimations[animname].mDefaultDuration = duration;
					}
					if(ret[0] == "k")
					{
						group = ret[1];
						keyframes = int(ret[2]);
					}
					if(ret[0] == "c")
					{	
						askal::Keyframe k;
						k.mGroupName = group;
						k.mAnimationName = animname;
						if(duration == 0)
						{
							duration = 1.0;
						}
						k.mTime = float(ret[2]) / duration;
						k.mQuaternion = gquat(float(ret[4]), float(ret[5]), float(ret[6]), float(ret[3]));

						
						if(! (s->mAnimations[animname].mActions.has_key(group)) )
						{
							harray<askal::Keyframe> ks;
							s->mAnimations[animname].mActions[group] = ks;
							
						}
						s->mAnimations[animname].mActions[group] += k;
					}
					
				} else if(state == 2)
				{
					harray<hstr> ret = lines[i].split(" ");
					if(ret[0] == "n")
					{
						if(_mMeshCache.has_key(ret[1]))
						{
							April::rendersys->logMessage("Mesh with the same name(" + ret[1] + "alredy exists!\n", "[askal]");
							exit(1);
						}
						if(m == NULL)
						{
							m = new AprilMesh();
							m->mName = ret[1];
							_mMeshCache[ret[1]] = m;
						}
					}
					if(ret[0] == "g")
					{
						m->mVertexGroups[int(ret[2])] = ret[1];
					}
					if(ret[0] == "v")
					{
						askal::Vertex v;
						v.mPosition.x = float(ret[2]);
						v.mPosition.y = float(ret[3]);
						v.mPosition.z = float(ret[4]);
						v.mNormal.x = float(ret[5]);
						v.mNormal.y = float(ret[6]);
						v.mNormal.z = float(ret[7]);
						int groups = int(ret[8]);
						askal::WeightBonePair wpb;
						gvec2 t; // first export them from dcc software
						v.mTexCoords = t;
						for(int i = 9; i < groups; ++i)
						{
							wpb.mBoneIndex = int(ret[i*2]);
							wpb.mWeight = float(ret[i*2 + 1]);
							v.mBoneIndices += wpb;
						}
						m->mVertices += v;
						//std::cout << "vec (" << v.mPosition.x << ", " << v.mPosition.y << ", " << v.mPosition.z << ")" << std::endl;
					}
					if(ret[0] == "f")
					{
						askal::Polygon p;
						p.mNormal.x = float(ret[5]);
						p.mNormal.y = float(ret[6]);
						p.mNormal.z = float(ret[7]);
						p.mVertexIndices[0] = int(ret[2]);
						p.mVertexIndices[1] = int(ret[3]);
						p.mVertexIndices[2] = int(ret[4]);
						m->mPolygons += p;
					}
				}
				
			}
		}
		if(s != NULL)
		{
			April::rendersys->logMessage(" Loaded skeleton and animation data from " + meshname, "[askal]");
		}
	}

}

